import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import java.awt.*; import java.io.*; import java.awt.geom.*; /** * A class that represents a simple picture. A simple picture may have * an associated file name and a title. A simple picture has pixels, * width, and height. A simple picture uses a BufferedImage to * hold the pixels. You can show a simple picture in a * PictureFrame (a JFrame). You can also explore a simple picture. * * @author Barb Ericson ericson@cc.gatech.edu */ public class SimplePicture implements DigitalPicture { /////////////////////// Fields ///////////////////////// /** * the file name associated with the simple picture */ private String fileName; /** * the title of the simple picture */ private String title; /** * buffered image to hold pixels for the simple picture */ private BufferedImage bufferedImage; /** * frame used to display the simple picture */ private PictureFrame pictureFrame; /** * extension for this file (jpg or bmp) */ private String extension; /////////////////////// Constructors ///////////////////////// /** * A Constructor that takes no arguments. It creates a picture with * a width of 200 and a height of 100 that is all white. * A no-argument constructor must be given in order for a class to * be able to be subclassed. By default all subclasses will implicitly * call this in their parent's no argument constructor unless a * different call to super() is explicitly made as the first line * of code in a constructor. */ public SimplePicture() {this(200,100);} /** * A Constructor that takes a file name and uses the file to create * a picture * @param fileName the file name to use in creating the picture */ public SimplePicture(String fileName) { // load the picture into the buffered image load(fileName); } /** * A constructor that takes the width and height desired for a picture and * creates a buffered image of that size. This constructor doesn't * show the picture. The pixels will all be white. * @param width the desired width * @param height the desired height */ public SimplePicture(int width, int height) { bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); title = "None"; fileName = "None"; extension = "jpg"; setAllPixelsToAColor(Color.white); } /** * A constructor that takes the width and height desired for a picture and * creates a buffered image of that size. It also takes the * color to use for the background of the picture. * @param width the desired width * @param height the desired height * @param theColor the background color for the picture */ public SimplePicture(int width, int height, Color theColor) { this(width,height); setAllPixelsToAColor(theColor); } /** * A Constructor that takes a picture to copy information from * @param copyPicture the picture to copy from */ public SimplePicture(SimplePicture copyPicture) { if (copyPicture.fileName != null) { this.fileName = new String(copyPicture.fileName); this.extension = copyPicture.extension; } if (copyPicture.title != null) this.title = new String(copyPicture.title); if (copyPicture.bufferedImage != null) { this.bufferedImage = new BufferedImage(copyPicture.getWidth(), copyPicture.getHeight(), BufferedImage.TYPE_INT_RGB); this.copyPicture(copyPicture); } } /** * A constructor that takes a buffered image * @param image the buffered image */ public SimplePicture(BufferedImage image) { this.bufferedImage = image; title = "None"; fileName = "None"; extension = "jpg"; } ////////////////////////// Methods ////////////////////////////////// /** * Method to get the extension for this picture * @return the extendsion (jpg, bmp, giff, etc) */ public String getExtension() { return extension; } /** * Method that will copy all of the passed source picture into * the current picture object * @param sourcePicture the picture object to copy */ public void copyPicture(SimplePicture sourcePicture) { Pixel sourcePixel = null; Pixel targetPixel = null; // loop through the columns for (int sourceX = 0, targetX = 0; sourceX < sourcePicture.getWidth() && targetX < this.getWidth(); sourceX++, targetX++) { // loop through the rows for (int sourceY = 0, targetY = 0; sourceY < sourcePicture.getHeight() && targetY < this.getHeight(); sourceY++, targetY++) { sourcePixel = sourcePicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } } /** * Method to set the color in the picture to the passed color * @param color the color to set to */ public void setAllPixelsToAColor(Color color) { // loop through all x for (int x = 0; x < this.getWidth(); x++) { // loop through all y for (int y = 0; y < this.getHeight(); y++) { getPixel(x,y).setColor(color); } } } /** * Method to get the buffered image * @return the buffered image */ public BufferedImage getBufferedImage() { return bufferedImage; } /** * Method to get a graphics object for this picture to use to draw on * @return a graphics object to use for drawing */ public Graphics getGraphics() { return bufferedImage.getGraphics(); } /** * Method to get a Graphics2D object for this picture which can * be used to do 2D drawing on the picture */ public Graphics2D createGraphics() { return bufferedImage.createGraphics(); } /** * Method to get the file name associated with the picture * @return the file name associated with the picture */ public String getFileName() { return fileName; } /** * Method to set the file name * @param name the full pathname of the file */ public void setFileName(String name) { fileName = name; } /** * Method to get the title of the picture * @return the title of the picture */ public String getTitle() { return title; } /** * Method to set the title for the picture * @param title the title to use for the picture */ public void setTitle(String title) { this.title = title; if (pictureFrame != null) pictureFrame.setTitle(title); } /** * Method to get the width of the picture in pixels * @return the width of the picture in pixels */ public int getWidth() { return bufferedImage.getWidth(); } /** * Method to get the height of the picture in pixels * @return the height of the picture in pixels */ public int getHeight() { return bufferedImage.getHeight(); } /** * Method to get the picture frame for the picture * @return the picture frame associated with this picture * (it may be null) */ public PictureFrame getPictureFrame() { return pictureFrame; } /** * Method to set the picture frame for this picture * @param pictureFrame the picture frame to use */ public void setPictureFrame(PictureFrame pictureFrame) { // set this picture objects' picture frame to the passed one this.pictureFrame = pictureFrame; } /** * Method to get an image from the picture * @return the buffered image since it is an image */ public Image getImage() { return bufferedImage; } /** * Method to return the pixel value as an int for the given x and y location * @param x the x coordinate of the pixel * @param y the y coordinate of the pixel * @return the pixel value as an integer (alpha, red, green, blue) */ public int getBasicPixel(int x, int y) { return bufferedImage.getRGB(x,y); } /** * Method to set the value of a pixel in the picture from an int * @param x the x coordinate of the pixel * @param y the y coordinate of the pixel * @param rgb the new rgb value of the pixel (alpha, red, green, blue) */ public void setBasicPixel(int x, int y, int rgb) { bufferedImage.setRGB(x,y,rgb); } /** * Method to get a pixel object for the given x and y location * @param x the x location of the pixel in the picture * @param y the y location of the pixel in the picture * @return a Pixel object for this location */ public Pixel getPixel(int x, int y) { // create the pixel object for this picture and the given x and y location Pixel pixel = new Pixel(this,x,y); return pixel; } /** * Method to get a one-dimensional array of Pixels for this simple picture * @return a one-dimensional array of Pixel objects starting with y=0 * to y=height-1 and x=0 to x=width-1. */ public Pixel[] getPixels() { int width = getWidth(); int height = getHeight(); Pixel[] pixelArray = new Pixel[width * height]; // loop through height rows from top to bottom for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) pixelArray[row * width + col] = new Pixel(this,col,row); return pixelArray; } /** * Method to get a two-dimensional array of Pixels for this simple picture * @return a two-dimensional array of Pixel objects in row-major order. */ public Pixel[][] getPixels2D() { int width = getWidth(); int height = getHeight(); Pixel[][] pixelArray = new Pixel[height][width]; // loop through height rows from top to bottom for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) pixelArray[row][col] = new Pixel(this,col,row); return pixelArray; } /** * Method to load the buffered image with the passed image * @param image the image to use */ public void load(Image image) { // get a graphics context to use to draw on the buffered image Graphics2D graphics2d = bufferedImage.createGraphics(); // draw the image on the buffered image starting at 0,0 graphics2d.drawImage(image,0,0,null); // show the new image show(); } /** * Method to show the picture in a picture frame */ public void show() { // if there is a current picture frame then use it if (pictureFrame != null) pictureFrame.updateImageAndShowIt(); // else create a new picture frame with this picture else pictureFrame = new PictureFrame(this); } /** * Method to hide the picture display */ public void hide() { if (pictureFrame != null) pictureFrame.setVisible(false); } /** * Method to make this picture visible or not * @param flag true if you want it visible else false */ public void setVisible(boolean flag) { if (flag) this.show(); else this.hide(); } /** * Method to open a picture explorer on a copy (in memory) of this * simple picture */ public void explore() { // create a copy of the current picture and explore it new PictureExplorer(new SimplePicture(this)); } /** * Method to force the picture to repaint itself. This is very * useful after you have changed the pixels in a picture and * you want to see the change. */ public void repaint() { // if there is a picture frame tell it to repaint if (pictureFrame != null) pictureFrame.repaint(); // else create a new picture frame else pictureFrame = new PictureFrame(this); } /** * Method to load the picture from the passed file name * @param fileName the file name to use to load the picture from * @throws IOException if the picture isn't found */ public void loadOrFail(String fileName) throws IOException { // set the current picture's file name this.fileName = fileName; // set the extension int posDot = fileName.indexOf('.'); if (posDot >= 0) this.extension = fileName.substring(posDot + 1); // if the current title is null use the file name if (title == null) title = fileName; File file = new File(this.fileName); if (!file.canRead()) { // try adding the media path file = new File(FileChooser.getMediaPath(this.fileName)); if (!file.canRead()) { throw new IOException(this.fileName + " could not be opened. Check that you specified the path"); } } bufferedImage = ImageIO.read(file); } /** * Method to read the contents of the picture from a filename * without throwing errors * @param fileName the name of the file to write the picture to * @return true if success else false */ public boolean load(String fileName) { try { this.loadOrFail(fileName); return true; } catch (Exception ex) { System.out.println("There was an error trying to open " + fileName); bufferedImage = new BufferedImage(600,200, BufferedImage.TYPE_INT_RGB); addMessage("Couldn't load " + fileName,5,100); return false; } } /** * Method to load the picture from the passed file name * this just calls load(fileName) and is for name compatibility * @param fileName the file name to use to load the picture from * @return true if success else false */ public boolean loadImage(String fileName) { return load(fileName); } /** * Method to draw a message as a string on the buffered image * @param message the message to draw on the buffered image * @param xPos the leftmost point of the string in x * @param yPos the bottom of the string in y */ public void addMessage(String message, int xPos, int yPos) { // get a graphics context to use to draw on the buffered image Graphics2D graphics2d = bufferedImage.createGraphics(); // set the color to white graphics2d.setPaint(Color.white); // set the font to Helvetica bold style and size 16 graphics2d.setFont(new Font("Helvetica",Font.BOLD,16)); // draw the message graphics2d.drawString(message,xPos,yPos); } /** * Method to draw a string at the given location on the picture * @param text the text to draw * @param xPos the left x for the text * @param yPos the top y for the text */ public void drawString(String text, int xPos, int yPos) { addMessage(text,xPos,yPos); } /** * Method to create a new picture by scaling the current * picture by the given * @param rFactor the amount to scale in the height (rows) * @param cFactor the amount to scale in the width (columns) * @return the resulting picture */ public Picture scale(double rFactor, double cFactor) { // set up the scale tranform AffineTransform scaleTransform = new AffineTransform(); scaleTransform.scale(cFactor,rFactor); // create a new picture object that is the right size Picture result = new Picture((int) (getHeight() * rFactor), (int) (getWidth() * cFactor)); // get the graphics 2d object to draw on the result Graphics graphics = result.getGraphics(); Graphics2D g2 = (Graphics2D) graphics; // draw the current image onto the result image scaled g2.drawImage(this.getImage(),scaleTransform,null); return result; } /** * Method to create a new picture of the passed width. * The aspect ratio of the width and height will stay * the same. * @param width the desired width * @return the resulting picture */ public Picture getPictureWithWidth(int width) { // set up the scale tranform double xFactor = (double) width / this.getWidth(); Picture result = scale(xFactor,xFactor); return result; } /** * Method to create a new picture of the passed height. * The aspect ratio of the width and height will stay * the same. * @param height the desired height * @return the resulting picture */ public Picture getPictureWithHeight(int height) { // set up the scale tranform double yFactor = (double) height / this.getHeight(); Picture result = scale(yFactor,yFactor); return result; } /** * Method to load a picture from a file name and show it in a picture frame * @param fileName the file name to load the picture from * @return true if success else false */ public boolean loadPictureAndShowIt(String fileName) { boolean result = true; // the default is that it worked // try to load the picture into the buffered image from the file name result = load(fileName); // show the picture in a picture frame show(); return result; } /** * Method to write the contents of the picture to a file with * the passed name * @param fileName the name of the file to write the picture to */ public void writeOrFail(String fileName) throws IOException { String extension = this.extension; // the default is current // create the file object File file = new File(fileName); File fileLoc = file.getParentFile(); // directory name // if there is no parent directory use the current media dir if (fileLoc == null) { fileName = FileChooser.getMediaPath(fileName); file = new File(fileName); fileLoc = file.getParentFile(); } // check that you can write to the directory if (!fileLoc.canWrite()) { throw new IOException(fileName + " could not be opened. Check to see if you can write to the directory."); } // get the extension int posDot = fileName.indexOf('.'); if (posDot >= 0) extension = fileName.substring(posDot + 1); // write the contents of the buffered image to the file as jpeg ImageIO.write(bufferedImage, extension, file); } /** * Method to write the contents of the picture to a file with * the passed name without throwing errors * @param fileName the name of the file to write the picture to * @return true if success else false */ public boolean write(String fileName) { try { this.writeOrFail(fileName); return true; } catch (Exception ex) { System.out.println("There was an error trying to write " + fileName); ex.printStackTrace(); return false; } } /** * Method to get the directory for the media * @param fileName the base file name to use * @return the full path name by appending * the file name to the media directory */ public static String getMediaPath(String fileName) { return FileChooser.getMediaPath(fileName); } /** * Method to get the coordinates of the enclosing rectangle after this * transformation is applied to the current picture * @return the enclosing rectangle */ public Rectangle2D getTransformEnclosingRect(AffineTransform trans) { int width = getWidth(); int height = getHeight(); double maxX = width - 1; double maxY = height - 1; double minX, minY; Point2D.Double p1 = new Point2D.Double(0,0); Point2D.Double p2 = new Point2D.Double(maxX,0); Point2D.Double p3 = new Point2D.Double(maxX,maxY); Point2D.Double p4 = new Point2D.Double(0,maxY); Point2D.Double result = new Point2D.Double(0,0); Rectangle2D.Double rect = null; // get the new points and min x and y and max x and y trans.deltaTransform(p1,result); minX = result.getX(); maxX = result.getX(); minY = result.getY(); maxY = result.getY(); trans.deltaTransform(p2,result); minX = Math.min(minX,result.getX()); maxX = Math.max(maxX,result.getX()); minY = Math.min(minY,result.getY()); maxY = Math.max(maxY,result.getY()); trans.deltaTransform(p3,result); minX = Math.min(minX,result.getX()); maxX = Math.max(maxX,result.getX()); minY = Math.min(minY,result.getY()); maxY = Math.max(maxY,result.getY()); trans.deltaTransform(p4,result); minX = Math.min(minX,result.getX()); maxX = Math.max(maxX,result.getX()); minY = Math.min(minY,result.getY()); maxY = Math.max(maxY,result.getY()); // create the bounding rectangle to return rect = new Rectangle2D.Double(minX,minY,maxX - minX + 1, maxY - minY + 1); return rect; } /** * Method to get the coordinates of the enclosing rectangle after this * transformation is applied to the current picture * @return the enclosing rectangle */ public Rectangle2D getTranslationEnclosingRect(AffineTransform trans) { return getTransformEnclosingRect(trans); } /** * Method to return a string with information about this picture * @return a string with information about the picture */ public String toString() { String output = "Simple Picture, filename " + fileName + " height " + getHeight() + " width " + getWidth(); return output; } } // end of SimplePicture class